package com.ling.web.video.extractor.utils

import android.annotation.SuppressLint
import android.util.Log
import com.ling.web.video.extractor.model.WebModel
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import org.apache.commons.lang3.StringEscapeUtils
import org.json.JSONObject
import java.net.URLDecoder
import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager

/**
 * @author Ling
 * @date :2024/11/22 16:04
 */
object WebExtractorUtil {

    private val YOUTUBE_WATCH_REGEX = Regex("(http|https)://(www\\.|m.|)youtube\\.com/watch\\?v=(.+?)")
    private val YOUTUBE_SHORT_REGEX = Regex("(http|https)://(www\\.|m.|)youtube\\.com/shorts/(.+?)")
    private val FACEBOOK_REGEX = Regex("(http|https)://(www\\.|m.|)facebook\\.com/(.+?)")
    private val VIMEO_REGEX = Regex("(http|https)://(www\\.|m.|)vimeo\\.com/(.+?)")
    private val BUZZVIDEO_REGEX = Regex("(http|https)://(www\\.|m.|)buzzvideos\\.com/(.+?)")
    private val IMDB_REGEX = Regex("(http|https)://(www\\.|m.|)imdb\\.com/(.+?)")
    private val TWITCH_REGEX = Regex("(http|https)://(www\\.|m.|)twitch\\.(tv|com)/(.+?)")
    private val INSTAGRAM_REGEX = Regex("(http|https)://(www\\.|m.|)instagram\\.com/(.+?)")
    private val YAHOO_REGEX = Regex("(http|https)://(.+?).yahoo\\.com/(.+?)")
    private val FOX_REGEX = Regex("(http|https)://(www\\.|m.|)foxnews\\.com/(.+?)")
    private val ESPN_REGEX = Regex("(http|https)://(.+?).espn\\.com/(.+?)")

    private val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
    private var oldTime = 0L


    fun getOkHttpClint(timeOutSecond : Long) : OkHttpClient {
        return OkHttpClient
            .Builder()
            .connectTimeout(timeOutSecond , TimeUnit.SECONDS)
            .readTimeout(timeOutSecond, TimeUnit.SECONDS)
            .writeTimeout(timeOutSecond, TimeUnit.SECONDS)
            .sslSocketFactory(getSSLSocketFactory(), getTrustManager())
            .hostnameVerifier { _, _ -> true }
            .build()
    }

    //设置计算开始时间
    fun setCurrentTime(){
        oldTime = System.currentTimeMillis()
    }

    //判断是否符合时间间隔
    fun isTimeOver(time : Long) : Boolean{
        val currentTime = System.currentTimeMillis()
        if(currentTime - oldTime >=  time){
            oldTime = currentTime
            return true
        }
        return false
    }

    fun transferYoutubeUrlToDefault(url : String) : String{
        if (url.contains("youtube.com/embed")) {
            val videoId = if (-1 != url.indexOf("?v=")) {
                url.substring(
                    url.indexOf("/embed/") + 7,
                    url.indexOf("?v=")
                )
            } else {
                url.substring(url.indexOf("/embed/") + 7)
            }
            val embedToWatchUrl = "https://www.youtube.com/watch?v=$videoId"
            return embedToWatchUrl
        }else if(url.contains("&list=")){
            return url.substring(0 ,url.indexOf("&list="))
        }
        return url
    }

    fun getApiRequest(url : String) : Request{
        val requestParams = JSONObject()
        requestParams.put("url" , url)

        return Request
            .Builder()
            .url("https://tubedown.cn/api/youtube")
            .post(requestParams.toString().toRequestBody("application/json".toMediaTypeOrNull()))
            .addHeader("User-Agent" , USER_AGENT)
            .build()
    }

    fun isYoutubeEmbed(url : String) : Boolean{
        return url.contains("youtube.com/embed" , true)
    }

    fun isYoutubeWebsite(url: String): Boolean {
        return if (url.isEmpty()) false else (YOUTUBE_WATCH_REGEX.matches(url) || YOUTUBE_SHORT_REGEX.matches(url))
    }

    fun isFaceBookWebsite(url: String): Boolean {
        return if (url.isEmpty()) false else FACEBOOK_REGEX.matches(url)
    }

    fun isVimeoWebsite(url: String): Boolean {
        return if (url.isEmpty()) false else VIMEO_REGEX.matches(url)
    }

    fun isBuzzVideoWebsite(url: String): Boolean {
        return if (url.isEmpty()) false else BUZZVIDEO_REGEX.matches(url)
    }

    fun isYahooWebsite(url: String): Boolean {
        return if (url.isEmpty()) false else YAHOO_REGEX.matches(url)
    }

    fun isFoxUrl(url: String): Boolean {
        return if (url.isEmpty()) false else FOX_REGEX.matches(url)
    }

    fun isImdbWebsite(url: String): Boolean {
        return if (url.isEmpty()) false else IMDB_REGEX.matches(url)
    }

    fun isTwitchWebsite(url: String): Boolean {
        return if (url.isEmpty()) false else TWITCH_REGEX.matches(url)
    }

    fun isInstagramWebsite(url: String): Boolean {
        return if (url.isEmpty()) false else INSTAGRAM_REGEX.matches(url)
    }

    fun isEspnWebsite(url: String): Boolean {
        return if (url.isEmpty()) false else ESPN_REGEX.matches(url)
    }

    fun transferUrlToRead(url: String): String {
        val newUrl = StringEscapeUtils.unescapeJava(url)
        if (newUrl.contains("&amp;")) {
            newUrl.replace("&amp;", "&")
        }
        return newUrl
    }

    fun parseJsonForApiResult(webUrl: String, json : String) : List<WebModel>?{
        try {
            val data = JSONObject(json).optJSONObject("data") ?: return null
            val videoList = arrayListOf<WebModel>()
            val thumb = data.optString("thumbnail" , "")
            val videoListMap = hashMapOf<String , String>()
            val videoArray = data.getJSONArray("formats")
            for(index in 0 until videoArray.length()){
                val item = videoArray.getJSONObject(index)
                val resolution = item.optString("resolution" ,"")
                if(resolution.isEmpty()) continue
                val height = item.optString("height" , "")
                val mime = item.optString("ext" , "")


                if(height.isEmpty() || !mime.contains("mp4")) continue

                if(videoListMap[resolution] == null){
                    var url = item.optString("url" , "")
                    if(url.isEmpty()) continue
                    //youtube排除m3u8格式的视频
                    if(isYoutubeWebsite(webUrl)){
                        if(item.optString("protocol" , "").contains("m3u8_native")) continue
                    }
                    url = URLDecoder.decode(url, "UTF-8")
                    url = transferUrlToRead(url)
                    videoListMap[resolution] = url
                    val qualityList = resolution.split("x")
                    val qualityInt = if(qualityList.isEmpty()) 0 else qualityList[0].toInt()
                    videoList.add(
                        WebModel(
                            resourceName = data.optString("title" , "") ,
                            mimeType = "video/mp4" ,
                            duration = dateStrToLong(data.optString("duration_string" , "00:00")),
                            quality = resolution,
                            qualityInt = qualityInt,
                            resourceCover = thumb,
                            parseFromUrl = webUrl ,
                            resourceUrl = url
                        )
                    )
                }
            }
            //如果有声音的链接，替换或添加有声音的链接，外部根据 audioFlag 来筛选需要的数据
            var audioUrl = data.optString("url" , "")
            val audioResolution = data.optString("resolution" , "")
            if(audioUrl.isNullOrEmpty() || audioResolution.isNullOrEmpty()) return videoList
            val qualityList = audioResolution.split("x")
            val qualityInt = if(qualityList.isEmpty()) 0 else qualityList[0].toInt()
            audioUrl = transferUrlToRead(audioUrl)
            val audioOld = videoList.find { it.quality == audioResolution }
            val audioMedia = WebModel(
                resourceName = data.optString("title" , ""),
                mimeType = "video/mp4" ,
                duration = dateStrToLong(data.optString("duration_string" , "00:00")),
                quality = audioResolution,
                qualityInt = qualityInt,
                audioFlag = true,
                resourceCover = thumb,
                parseFromUrl = webUrl,
                resourceUrl = audioUrl
            )
            if(audioOld == null){
                videoList.add(audioMedia)
            }else{
                videoList.remove(audioOld)
                videoList.add(audioMedia)
            }
            videoList.sortBy { it.qualityInt }
            return videoList
        } catch (e: Exception) {
            Log.e("---->", "parseJsonForApiResult: error${e.message}")
            return null
        }
    }

    fun isFlitterResource(url : String) : Boolean{
        return url.contains(".png") || url.contains(".jpg") || url.contains(".otf")
                || url.contains(".jpeg") || url.contains(".woff") || url.contains(".js")
                || url.contains(".ico") || url.contains(".gif") || url.contains(".css")
                || url.contains(".json") || url.contains(".html") || url.contains(".svg")
                || url.contains(".ts")
                || url.contains("https://i.vimeocdn.com") || url.contains("fresnel-events.vimeocdn.com")
                || url.contains("www.googletagmanager.com") || url.contains("g.doubleclick.net")
                || url.contains("https://sio-1.livestream.com/socket.io")
                || url.contains("www.google-analytics.com") || url.contains("vimeo.com/ablincoln/fatal_attraction")
    }



    fun getApiEnableByApiMode(webUrl: String) : Boolean{
        return isYoutubeWebsite(webUrl)
                || isFaceBookWebsite(webUrl)
                || isVimeoWebsite(webUrl)
                || isInstagramWebsite(webUrl)
                || isTwitchWebsite(webUrl)
                || isImdbWebsite(webUrl)
                || isBuzzVideoWebsite(webUrl)
                || isFoxUrl(webUrl)
    }

    fun getApiEnableByResourceMode(webUrl: String) : Boolean{
        return isYoutubeWebsite(webUrl) || isFoxUrl(webUrl)
    }




    private fun dateStrToLong(timeStr : String) : Long{
        try {
            val timeArr = timeStr.split(":")
            when (timeArr.size) {
                3 -> {
                    val time = (timeArr[0].toInt() * 3600000) + (timeArr[1].toInt() * 60000) + (timeArr[2].toInt() * 1000)
                    return time.toLong()
                }
                2 -> {
                    val time = (timeArr[0].toInt() * 60000) + (timeArr[1].toInt() * 1000)
                    return time.toLong()
                }
                else -> {
                    return timeStr.toLong() * 1000
                }
            }
        } catch (e: Exception) {
            Log.e("--->", "dateStrToLong: ${e.message}")
        }
        return 0
    }

    @Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
    private fun getSSLSocketFactory(): SSLSocketFactory {
        val sslContext = SSLContext.getInstance("SSL")
        sslContext.init(null, arrayOf(getTrustManager()), SecureRandom())
        return sslContext.socketFactory
    }

    @SuppressLint("TrustAllX509TrustManager", "CustomX509TrustManager")
    private fun getTrustManager() : X509TrustManager {
        return object : X509TrustManager {
            override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}

            override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}

            override fun getAcceptedIssuers(): Array<X509Certificate> {
                return arrayOf()
            }
        }
    }
}